//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖保佑             永无BUG 
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//                  不见满街漂亮妹，哪个归得程序员？  


/* ****************************************************************************
 * Author: 671643387
 * Date: 2015/6/17
 * ***************************************************************************/

/******************************************************************************
 * 审计相关
 */


/**
 * 玩家充值情况
 * @param query
 * @param resp
 * @param callback
 */
exports.pay = function(query, resp, callback){
    var minTime = +query.minTime;   // 查询起始时间
    var maxTime = +query.maxTime;   // 查询结束时间
    var openidStr = query.openids;

    // 检查参数
    if (isNaN(minTime) || isNaN(maxTime) || !openidStr)
    {
        resp.code = 1; resp.desc = 'invalid params';
        callback(); return;
    }

    // 需要查询的玩家ID数组
    var openidArr = openidStr.split(",");

    minTime = Date.createFromStamp(minTime).zeroMonth().getStamp();
    maxTime = Date.createFromStamp(maxTime).zeroMonth().getStamp();

    LOG("sum.pay");
    LOG("minTime: " + minTime);
    LOG("maxTime: " + maxTime);

    var _Sum = [getTableHeader(minTime, maxTime)];

    async.eachSeries(openidArr, function(openid, esCb){
        var _userSum = [];
        async.each(gServers.getSids(), function(sid, cb) {
            _queryPay(openid, sid, minTime, maxTime, function(err, res){
                if (err) console.error(util.format("%j", err));
                else {
                    _userSum.push(res);
                }
                cb(null);
            });
        }, function(err){
            _Sum.push([openid].concat(_combinePay(_userSum, minTime, maxTime)));
            esCb(null);
        });
    }, function(err){
        resp.plain = true;
        resp.data = _Sum.join("\n");
        callback();
    });

    function getTableHeader(minTime, maxTime) {
        var header = ["openid", "首次充值时间", "截至充值时间"];
        var curStamp = minTime;
        while(curStamp < maxTime) {
            var timeStr = Date.createFromStamp(curStamp).format("yyyy-MM");
            header.push(timeStr + "充值次数");
            header.push(timeStr + "充值金额");
            curStamp = Date.nextMonth(curStamp);
        }
        header.push("总充值金额");
        return header;
    }
};


/**
 * 玩家消费情况
 * @param query
 * @param resp
 * @param callback
 */
exports.cost = function(query, resp, callback){
    var minTime = +query.minTime;   // 查询起始时间
    var maxTime = +query.maxTime;   // 查询结束时间
    var openidStr = query.openids;

    // 检查参数
    if (isNaN(minTime) || isNaN(maxTime) || !openidStr)
    {
        resp.code = 1; resp.desc = 'invalid params';
        callback(); return;
    }

    // 需要查询的玩家ID数组
    var openidArr = openidStr.split(",");

    minTime = Date.createFromStamp(minTime).zeroMonth().getStamp();
    maxTime = Date.createFromStamp(maxTime).zeroMonth().getStamp();

    var _Sum = [getTableHeader(minTime, maxTime)];

    async.eachSeries(openidArr, function(openid, esCb){
        var _userSum = [];
        async.each(gServers.getSids(), function(sid, cb) {
            _queryCost(openid, sid, minTime, maxTime, function(err, res){
                if (err) console.error(util.format("%j", err));
                else {
                    _userSum.push(res);
                }
                cb(null);
            });
        }, function(err){
            _Sum.push([openid].concat(_combineCost(_userSum, minTime, maxTime)));
            esCb(null);
        });
    }, function(err){
        resp.plain = true;
        resp.data = _Sum.join("\n");
        callback();
    });

    function getTableHeader(minTime, maxTime) {
        var header = ["openid", "首次消费时间", "截至消费时间"];
        var curStamp = minTime;
        while(curStamp < maxTime) {
            var timeStr = Date.createFromStamp(curStamp).format("yyyy-MM");
            header.push(timeStr + "消费次数");
            header.push(timeStr + "消费元宝");
            curStamp = Date.nextMonth(curStamp);
        }
        header.push("总消费次数");
        header.push("总消费元宝");
        header.push("剩余金币");
        header.push("剩余元宝");
        return header;
    }
};

/**
 * 查询玩家注册时间/登录时间之类数据
 * @param query
 * @param resp
 * @param callback
 */
exports.playerTimes = function(query, resp, callback){
    var openidStr = query.openids;

    // 检查参数
    if (!openidStr)
    {
        resp.code = 1; resp.desc = 'invalid params';
        callback(); return;
    }

    // 需要查询的玩家ID数组
    var openidArr = openidStr.split(",");

    var _Sum = ["openid,注册时间,最后登录时间"];

    async.eachSeries(openidArr, function(openid, esCb){
        var pRegTime = 0;
        var pLogTime = 0;
        async.each(gServers.getSids(), function(sid, cb) {
            _queryUserTimes(openid, function(err, res){
                if (err) {
                    LOG(err);
                } else {
                    var psRegTime = res[0];
                    var psLogTime = res[1];
                    if (pRegTime == 0 || pRegTime > psRegTime) {
                        pRegTime = psRegTime;
                    }
                    if (pLogTime < psLogTime) {
                        pLogTime = psLogTime;
                    }
                }
                cb();
            });
        }, function(err){
            _Sum.push(openid + "," + Date.createFromStamp(pRegTime).stdFormatedString() + "," + pLogTime);
            esCb();
        });
    }, function(err){
        resp.plain = true;
        resp.data = _Sum.join("\n");
        callback();
    });
};

/**
 * 查询玩家剩余元宝
 * @param query
 * @param resp
 * @param callback
 */
exports.playerRemainCash = function(query, resp, callback){
    var minTime = +query.minTime;   // 查询起始时间
    var maxTime = +query.maxTime;   // 查询结束时间
    var openidStr = query.openids;

    // 检查参数
    if (isNaN(minTime) || isNaN(maxTime) || !openidStr)
    {
        resp.code = 1; resp.desc = 'invalid params';
        callback(); return;
    }

    // 需要查询的玩家ID数组
    var openidArr = openidStr.split(",");
    var playerMap = {};
    openidArr.forEach(function(openid){
        playerMap[openid] = 0;
    });

    async.each(gServers.getSids(), function(sid, cb) {
        _queryCashRemain(minTime, maxTime, openidArr, sid, function(err, res){
            if (err) {
                LOG(err);
            } else {
                for (var openid in res) {
                    playerMap[openid] += res[openid];
                }
            }
            cb(null);
        });
    }, function(err){
        var sum = ["openid,剩余元宝"];
        for (var openid in playerMap) {
            sum.push(openid + "," + playerMap[openid]);
        }
        resp.plain = true;
        resp.data = sum.join("\n");
        callback();
    });
};


///////////////////////////////////////////////////////////////////////////////
// 帮助函数

/**
 * 在sid服务器查询玩家数据
 * @param openid
 * @param sid
 * @param minTime
 * @param maxTime
 * @param fn
 * @private
 */
function _queryPay(openid, sid, minTime, maxTime, fn) {
    var ret = {
        minCT:0,
        maxCT:0,
        sumCC:0,
        data:{}
    };

    var db = gServers.getGameDB(sid);
    if (!db) {
        fn("no db found for " + sid); return;
    }

    db.collection('pay').find(
        {
            "openid":openid,
            "ts":{
                "$gte":minTime + "",
                "$lt":maxTime + ""
            }
        }
    ).toArray(function(err, arr){
            if (err) fn(err);
            else {
                arr.forEach(function(item){
                    var ts = +item.ts;
                    var monthStamp = Date.createFromStamp(ts).zeroMonth().getStamp();

                    // 更新当月数据
                    var _data = ret.data[monthStamp] || {"count":0, "cash":0};
                    _data.count++; _data.cash += +item.amt;
                    ret.data[monthStamp] = _data;

                    // 统计总充值
                    ret.sumCC += +item.amt;

                    // 更新首次充值时间
                    if ((ts < ret.minCT && ret.minCT != 0) || (ret.minCT == 0)) {
                        ret.minCT = ts;
                    }

                    // 更新截至充值时间
                    if ((ts > ret.maxCT && ret.maxCT != 0) || (ret.maxCT == 0)) {
                        ret.maxCT = ts;
                    }
                });

                fn(null, ret);
            }
        }
    );
}

/**
 * 合并一个玩家的数据
 * @param resArr
 * @param minTime
 * @param maxTime
 * @private
 */
function _combinePay(resArr, minTime, maxTime) {
    var minChargeTime = 0;
    var maxChargeTime = 0;
    var chargeSum = 0;
    var data = {};
    resArr.forEach(function(item){

        if (item.minCT != 0) {

            if ((item.minCT < minChargeTime && minChargeTime != 0) || (minChargeTime == 0)) {
                minChargeTime = item.minCT;
            }
            if ((item.maxCT > maxChargeTime && maxChargeTime != 0) || (maxChargeTime == 0)) {
                maxChargeTime = item.maxCT;
            }

            for (var key in item.data) {
                var _data = data[key] || {"count":0, "cash":0};
                _data.count += item.data[key].count;
                _data.cash += item.data[key].cash;
                data[key] = _data;
            }

        }

    });

    var ret = [Date.createFromStamp(minChargeTime).format("yyyy/MM/dd hh:mm"),
        Date.createFromStamp(maxChargeTime).format("yyyy/MM/dd hh:mm")];

    var curStamp = +minTime;
    while (curStamp < +maxTime) {
        var _d = data[curStamp] || { "count":0, "cash":0 };
        //ret.push("'" + Date.createFromStamp(curStamp).format("yyyy-MM") + "'");
        ret.push(_d.count);
        ret.push(_d.cash * config.platform.exchange_rate);

        chargeSum += _d.cash;

        curStamp = Date.nextMonth(curStamp);
    }

    //for (var key in data) {
    //    ret.push("'" + Date.createFromStamp(+key).format("yyyy-MM") + "'");
    //    ret.push(data[key].count);
    //    ret.push(data[key].cash * config.platform.exchange_rate);
    //}
    ret.push(chargeSum * config.platform.exchange_rate);
    return ret;
}


/**
 * 在sid服务器查询玩家数据
 * @param openid
 * @param sid
 * @param minTime
 * @param maxTime
 * @param fn
 * @private
 */
function _queryCost(openid, sid, minTime, maxTime, fn) {
    var ret = {
        minCT:0,    // 首次充值时间
        maxCT:0,    // 截至充值时间
        sumCC:0,    // 总充值
        gold:0,     // 当前剩余金币
        cash:0,     // 当前剩余元宝
        data:{}
    };

    var db = gServers.getGameDB(sid);
    if (!db) {
        fn("no db found for " + sid); return;
    }

    db.collection('plat').find(
        {
            "_id":openid
        }
    ).toArray(function(err, res){
            if (err) fn(err);
            else {
                if (res.length <= 0) {
                    fn(null, ret);
                    return;
                }

                if (res[0].uids) {
                    async.eachSeries(res[0].uids, function(uid, esFn){
                        _queryUidCost(uid, function(){
                            esFn(null);
                        });
                    }, function(err){
                        fn(null, ret);
                    });
                } else if (res[0].uid) {
                    _queryUidCost(res[0].uid, function(){
                        fn(null, ret);
                    });
                } else {
                    fn(null, ret);
                }
            }
    });

    function _queryUidCost(uid, _fn) {
        async.series([
            function(cb) {
                db.collection('cash').find(
                    {
                        "uid":uid,
                        "time":{
                            "$gte":minTime,
                            "$lt":maxTime
                        }
                    }
                ).toArray(function(err, arr){
                        if (err) cb(err);
                        else {
                            arr.forEach(function(item){

                                if (item.num > 0) return; // 统计的是消费情况

                                var ts = +item.time;
                                var monthStamp = Date.createFromStamp(ts).zeroMonth().getStamp();

                                // 更新当月数据
                                var _data = ret.data[monthStamp] || {"count":0, "cash":0};
                                _data.count++; _data.cash += Math.abs(+item.num);
                                ret.data[monthStamp] = _data;

                                // 统计总消费
                                ret.sumCC += Math.abs(+item.num);

                                // 更新首次消费时间
                                if ((ts < ret.minCT && ret.minCT != 0) || (ret.minCT == 0)) {
                                    ret.minCT = ts;
                                }

                                // 更新截至消费时间
                                if ((ts > ret.maxCT && ret.maxCT != 0) || (ret.maxCT == 0)) {
                                    ret.maxCT = ts;
                                }
                            });

                            cb(null);
                        }
                    }
                );
            },
            function(cb) {
                db.collection('user').find({_id:uid}).toArray(function(err, res){
                    if (!err && res.length == 1) {
                        var user = res[0];
                        var _gold = +user.status.gold;
                        if (!isNaN(_gold)) {
                            ret.gold += _gold;
                        }
                        var _cash = +user.status.cash;
                        if (!isNaN(_cash)) {
                            ret.cash += _cash;
                        }
                    }
                    cb(null);
                });
            }
        ], function(err){
            _fn(err);
        });

    }
}

/**
 * 合并一个玩家的数据
 * @param resArr
 * @param minTime
 * @param maxTime
 * @private
 */
function _combineCost(resArr, minTime, maxTime) {
    var minCostTime = 0;
    var maxCostTime = 0;
    var costSum = 0;
    var costTimes = 0;
    var goldSum = 0;    // 玩家剩余金币总数
    var cashSum = 0;    // 玩家剩余元宝总数
    var data = {};
    resArr.forEach(function(item){

        if (item.minCT != 0) {

            if ((item.minCT < minCostTime && minCostTime != 0) || (minCostTime == 0)) {
                minCostTime = item.minCT;
            }
            if ((item.maxCT > maxCostTime && maxCostTime != 0) || (maxCostTime == 0)) {
                maxCostTime = item.maxCT;
            }

            for (var key in item.data) {
                var _data = data[key] || {"count":0, "cash":0};
                _data.count += item.data[key].count;
                _data.cash += item.data[key].cash;
                data[key] = _data;
            }
        }

        goldSum += +item.gold;
        cashSum += +item.cash;
    });

    var ret = [Date.createFromStamp(minCostTime).format("yyyy/MM/dd hh:mm"),
        Date.createFromStamp(maxCostTime).format("yyyy/MM/dd hh:mm")];

    var curStamp = +minTime;
    while (curStamp < +maxTime) {
        var _d = data[curStamp] || { "count":0, "cash":0 };
        //ret.push("'" + Date.createFromStamp(curStamp).format("yyyy-MM") + "'");
        ret.push(_d.count);
        ret.push(_d.cash);

        costSum += _d.cash;
        costTimes += _d.count;

        curStamp = Date.nextMonth(curStamp);
    }

    //for (var key in data) {
    //    ret.push("'" + Date.createFromStamp(+key).format("yyyy-MM") + "'");
    //    ret.push(data[key].count);
    //    ret.push(data[key].cash);
    //
    //    costSum += data[key].cash;
    //    costTimes += data[key].count;
    //}

    ret.push(costTimes);    // 总消费次数
    ret.push(costSum);  // 总消费额
    ret.push(goldSum);  // 剩余金币
    ret.push(cashSum);  // 剩余元宝
    return ret;
}

/**
 * 查询玩家注册时间
 * @param openid
 * @param fn
 */
function _queryUserTimes(openid, fn) {
    var regTime = 0;                // 玩家最早注册时间
    var logTime = 0;                // 玩家最后登录时间
    async.series([
        function(cb) {
            async.each(gServers.getSids(), function(sid, esCb) {
                var db = gServers.getGameDB(sid);
                if (!db) {
                    esCb();
                    return;
                }

                var dbPlat = db.collection("plat");
                dbPlat.find({"_id":openid}).toArray(function(err, res){
                    if (err) {
                        LOG(err);
                        esCb();
                    }
                    else if (res) {
                        var _plat = res[0];
                        if (_plat) {
                            if (_plat.uids) {
                                async.eachSeries(_plat.uids, function(uid, _esCb){
                                    _getCharaInfo(db, sid, uid, function(err, res){
                                        if (!err) {
                                            var cRegTime = res[0];
                                            var cLogTime = res[1];
                                            if (regTime == 0 || regTime > cRegTime) {
                                                regTime = cRegTime;
                                            }
                                            if (logTime < cLogTime) {
                                                logTime = cLogTime;
                                            }
                                        }
                                        _esCb();
                                    });
                                }, function(err, res){
                                    esCb();
                                });
                            } else {
                                _getCharaInfo(db, sid, _plat.uid, function(err, res){
                                    if (!err) {
                                        var cRegTime = res[0];
                                        var cLogTime = res[1];
                                        if (regTime == 0 || regTime > cRegTime) {
                                            regTime = cRegTime;
                                        }
                                        if (logTime < cLogTime) {
                                            logTime = cLogTime;
                                        }
                                    }
                                    esCb();
                                });
                            }
                        } else {
                            esCb();
                        }
                    }
                });
            }, function(err){
                cb();
            });
        }
    ], function(err){
        fn(err, [regTime, logTime]);
    });

    /**
     * 查询角色创建时间
     * @param db
     * @param sid
     * @param uid
     * @param fn
     * @private
     */
    function _getCharaInfo(db, sid, uid, fn){
        db.collection("user").find({_id:uid}).toArray(function(err, res){
            if (err) fn(err);
            else {
                if (res && res.length > 0) {
                    var charaInfo = res[0];
                    fn(null, [
                        charaInfo.info.create,  // 角色创建时间
                        charaInfo.mark.day      // 角色上次登录时间
                    ]);
                    return;
                }
                fn("NO PLAYER " + sid);
            }
        });
    }
}

/**
 * 查询玩家剩余元宝
 * @param minTime
 * @param maxTime
 * @param openids
 * @param sid
 * @param fn
 * @private
 */
function _queryCashRemain(minTime, maxTime, openids, sid, fn){
    var db = gServers.getGameDB(sid);
    if (!db) {
        fn("no db found for " + sid); return;
    }

    var uids = [];
    var uidMap = {};
    var retMap = {};

    async.series([
        // 查询uid列表
        function(cb) {
            async.eachSeries(openids, function(openid, esCb){
                db.collection('plat').find({"_id":openid}).toArray(function(err, res) {
                    if (err) {
                        LOG(err);
                    } else {
                        var plat = res[0];
                        if (plat) {
                            var puids = [];
                            if (plat.uids) puids = plat.uids;
                            else puids.push(plat.uid);
                            puids.forEach(function(uid){
                                uidMap[uid] = openid;
                            });
                            uids = uids.concat(puids);
                        }
                    }
                    esCb(null);
                });
            }, function(err){
                cb(null);
            });
        },
        // 查询剩余元宝
        function(cb) {
            db.collection('cash').group(
                {uid:1},
                {
                    "uid":{
                        "$in":uids
                    },
                    "time":{
                        "$gte":minTime,
                        "$lt":maxTime
                    }
                },
                {cash:0, time:0},
                function(doc, prev){
                    var docTime = +doc.time;
                    if (prev.time == 0 || prev.time < docTime) {
                        prev.time = docTime;
                        prev.cash = doc.remain;
                    }
                },
                function(err, res){
                    if (err) cb(err);
                    else {
                        res.forEach(function(item){
                            var openid = uidMap[item.uid];
                            var pSum = retMap[openid] || 0;
                            pSum += item.cash;
                            retMap[openid] = pSum;
                        });
                        cb(null);
                    }
                }
            );
        }
    ], function(err){
        fn(err, retMap);
    });
}